package com.pactera.asmp.server.schedule;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.pactera.asmp.server.common.constants.ScreenRedisKey;
import com.pactera.asmp.server.dao.TaskMapper;
import com.pactera.asmp.server.entity.ProjectStatisticsDataDTO;
import com.pactera.asmp.server.entity.TaskExceTimeDataDTO;
import com.pactera.asmp.server.utils.DateUtil;

@Service
@EnableScheduling
public class TaskSchedule {

    private Logger logger = LoggerFactory.getLogger(TaskSchedule.class);

    @Autowired
    private TaskMapper taskMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final DateTimeFormatter FORMAT_YYYYMM = DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_YYYYMM);

    private static final DateTimeFormatter FORMAT_YYYYMMDD = DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_YYYY_MM_DD);

    private static final DateTimeFormatter FORMAT_YYYYMMDDHHMMSS = DateTimeFormatter.ofPattern(DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS);

    /**
     * 计算上个月成功率（每个月一日0:01:00执行）
     */
    @Scheduled(cron = "0 1 0 1 * ?")
    public void calculateLastMonthSuccessRate(){
        logger.info("============ calculate last month success rate start ============");
        LocalDate now = LocalDate.now();
        logger.info("current date : {}" , now.toString());
        LocalDate last = now.minusMonths(1);
        String startDateStr = last.withDayOfMonth(1).format(DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_YYYY_MM_DD));
        String endDateStr = now.withDayOfMonth(1).format(DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_YYYY_MM_DD));
        // 上个月yyyyMM
        String monthStr = last.format(DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_YYYYMM));
        // 上上个月yyyyMM
        String lastStr = last.minusMonths(1).format(DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_YYYYMM));

        LocalDateTime startDateTime = LocalDateTime.parse(startDateStr + " 00:00:00", FORMAT_YYYYMMDDHHMMSS);
        LocalDateTime endDateTime = LocalDateTime.parse(endDateStr + " 00:00:00", FORMAT_YYYYMMDDHHMMSS);
        List<ProjectStatisticsDataDTO> dataDTOs= taskMapper.summaryTaskByMonth(startDateTime, endDateTime);
        if(CollectionUtils.isEmpty(dataDTOs)) {
            logger.info("no data, startTime:{}, endTime:{}.", startDateTime, endDateTime);
            logger.info("============ calculate last month success rate end ============");
            return;
        }
        dataDTOs.stream().forEach(dataDTO -> {
            BigDecimal lastRate = null;
            if(dataDTO.getExeNum() == null || dataDTO.getExeNum() == 0L) {
                lastRate = BigDecimal.ZERO;
            }else {
                lastRate = new BigDecimal(dataDTO.getSuccessNum()).divide(new BigDecimal(dataDTO.getExeNum()), 4, BigDecimal.ROUND_HALF_UP);
            }
            // 获取上上个月的成功率
            Object rateObj = redisTemplate.opsForHash().get(ScreenRedisKey.REDIS_KEY_SCREEN_TASK_LASTMONTH_SUCCESSRATE + lastStr, dataDTO.getProjectId().toString());
            BigDecimal rate;
            if(null == rateObj) {
                rate = BigDecimal.ZERO;
            }else {
                rate = new BigDecimal(rateObj.toString());
            }
            BigDecimal promoteRate = lastRate.subtract(rate);
            redisTemplate.opsForHash().put(ScreenRedisKey.REDIS_KEY_SCREEN_TASK_LASTMONTH_PROMOTERATE + monthStr, dataDTO.getProjectId().toString(), promoteRate.toString());
            redisTemplate.opsForHash().put(ScreenRedisKey.REDIS_KEY_SCREEN_TASK_LASTMONTH_SUCCESSRATE + monthStr, dataDTO.getProjectId().toString(), lastRate.toString());
        });
        logger.info("============ calculate last month success rate end ============");
    }

    /**
     * 台架执行任务数（每天执行一次）
     */
    @Scheduled(cron = "0 1 0 * * ?")
    public void calculateTaskExceTime(){
        logger.info("============ calculate task exce times start ============");
        // 第一次执行时统计之前所有月份
        if(!redisTemplate.hasKey(ScreenRedisKey.REDIS_KEY_SCREEN_TASK_EXCETIME_BY_MASTERCOMPUTE.concat("*"))) {
            List<TaskExceTimeDataDTO> taskExceTimeDataDTOs = taskMapper.summaryAllTaskExceTimeByMasterComputer(null, null);
            Map<Integer, List<TaskExceTimeDataDTO>> mapByProjectId = taskExceTimeDataDTOs.stream().collect(Collectors.groupingBy(TaskExceTimeDataDTO::getProjectId));
            for(Integer projectId:mapByProjectId.keySet()) {
                Map<Integer, List<TaskExceTimeDataDTO>> mapByMasterComputerId = mapByProjectId.get(projectId).stream().collect(Collectors.groupingBy(TaskExceTimeDataDTO::getMasterComputerId));
                for(Integer masterComputerId:mapByMasterComputerId.keySet()) {
                    redisTemplate.opsForHash().put(ScreenRedisKey.REDIS_KEY_SCREEN_TASK_EXCETIME_BY_MASTERCOMPUTE + projectId, masterComputerId.toString(), JSONObject.toJSONString(mapByMasterComputerId.get(masterComputerId)));
                }
            }
        }else {
            LocalDateTime lastDay = LocalDateTime.now().minusDays(-1L);
            LocalDateTime startTime = LocalDateTime.parse(lastDay.format(FORMAT_YYYYMMDD), FORMAT_YYYYMMDDHHMMSS);
            LocalDateTime endTime = startTime.plusDays(1L);
            String exceMonth = startTime.format(FORMAT_YYYYMM);

            List<TaskExceTimeDataDTO> taskExceTimeDataDTOs = taskMapper.summaryAllTaskExceTimeByMasterComputer(startTime, endTime);
            Map<Integer, List<TaskExceTimeDataDTO>> mapByProjectId = taskExceTimeDataDTOs.stream().collect(Collectors.groupingBy(TaskExceTimeDataDTO::getProjectId));
            for(Integer projectId:mapByProjectId.keySet()) {
                Map<Integer, List<TaskExceTimeDataDTO>> mapByMasterComputerId = mapByProjectId.get(projectId).stream().collect(Collectors.groupingBy(TaskExceTimeDataDTO::getMasterComputerId));
                for(Integer masterComputerId:mapByMasterComputerId.keySet()) {
                    Object obj = redisTemplate.opsForHash().get(ScreenRedisKey.REDIS_KEY_SCREEN_TASK_EXCETIME_BY_MASTERCOMPUTE + projectId, masterComputerId.toString());
                    if(null == obj) {
                        redisTemplate.opsForHash().put(ScreenRedisKey.REDIS_KEY_SCREEN_TASK_EXCETIME_BY_MASTERCOMPUTE + projectId, masterComputerId.toString(), JSONObject.toJSONString(mapByMasterComputerId.get(masterComputerId)));
                    }else {
                        List<TaskExceTimeDataDTO> curTaskExceTimeDataDTOs = JSON.parseObject(obj.toString(), new TypeReference<List<TaskExceTimeDataDTO>>() {});
                        curTaskExceTimeDataDTOs.stream().filter(x -> StringUtils.equals(x.getExceMonth(), exceMonth)).forEach(x ->{
                            x.setExceTime(x.getExceTime().longValue() + mapByMasterComputerId.get(masterComputerId).get(0).getExceTime().longValue());
                        });
                    }
                }
            }
        }
        logger.info("============ calculate task exce times end ============");
    }
}
